8.5.3 ジェネリック関数とジェネリックなデータ構造の利用
8章で学んだことを使用して、どんな具象型にも対応できる二本木構造を作る
まず「2つの値を比較してその順序を返す」ジェネリック関数を定義する
code:go
type OrderableFuncT any func(t1, t2 T) int
次にTree構造体と新しいTreeを生成するファクトリの定義をする
code:go
type TreeT any struct {
f OrderableFuncT
root *NodeT
}
type NodeT any struct {
val T
left, right *NodeT
}
func NewTreeT any(f OrderableFuncT) *TreeT {
return &TreeT{
f: f,
}
}
TreeのメソッドはNodeのメソッドをラップする
code:go
func (t *TreeT) Add(v T) {
t.root = t.root.Add(t.f, v)
}
func (t *TreeT) Contains(v T) bool {
return t.root.Contains(t.f, v)
}
Nodeのメソッドで要素の順序付けに使う関数を受け取る
code:go
func (n *NodeT) Add(f OrderableFuncT, v T) *NodeT {
if n == nil {
return &NodeT{val: v}
}
switch r := f(v, n.val); {
case r <= -1:
n.left = n.left.Add(f, v)
case r >= 1:
n.right = n.right.Add(f, v)
}
return n
}
func (n *NodeT) Contains(f OrderableFuncT, v T) bool {
if n == nil {
return false
}
switch r := f(v, n.val); {
case r <= -1:
return n.left.Contains(f, v)
case r >= 1:
return n.right.Contains(f, v)
}
return true
}
組み込み関数を使用する場合
code:go
func main() {
t1 := NewTree(cmp.Compareint)
t1.Add(10)
t1.Add(30)
t1.Add(15)
fmt.Println(t1.Contains(15)) // true
fmt.Println(t1.Contains(40)) // false
}
構造体を使用するパターン1 構造体を受け取る関数を定義
code:go
func main() {
t2 := NewTree(OrderPeople)
t2.Add(Person{"Bob", 30})
t2.Add(Person{"Maria", 35})
t2.Add(Person{"Bob", 50})
fmt.Println(t2.Contains(Person{"Bob", 30})) // true
fmt.Println(t2.Contains(Person{"Fred", 25})) // false
}
type Person struct {
Name string
Age int
}
func OrderPeople(p1, p2 Person) int {
out := cmp.Compare(p1.Name, p2.Name)
if out == 0 {
out = cmp.Compare(p1.Age, p2.Age)
}
return out
}
構造体を使用するパターン2 メソッドを定義
code:go
func main() {
t3 := NewTree(Person.Order)
t3.Add(Person{"Bob", 30})
t3.Add(Person{"Maria", 35})
t3.Add(Person{"Bob", 50})
fmt.Println(t3.Contains(Person{"Bob", 30})) // true
fmt.Println(t3.Contains(Person{"Fred", 25})) // false
}
type Person struct {
Name string
Age int
}
func (p Person) Order(other Person) int {
out := cmp.Compare(p.Name, other.Name)
if out == 0 {
out = cmp.Compare(p.Age, other.Age)
}
return out
}